{#
  Template for structure representing a composite entity in Kafka protocol (e.g. FetchRequest).
  Rendered templates for each structure in Kafka protocol will be put into 'requests.h' file.

  Each structure is capable of holding all versions of given entity (what means its fields are
  actually a superset of union of all versions' fields). Each version has a dedicated deserializer
  (named $requestV$versionDeserializer), which calls the matching constructor.

  To serialize, it is necessary to pass the encoding context (that contains the version that's
  being serialized). Depending on the version, the fields will be written to the buffer.
#}
struct {{ complex_type.name }} {

  {#
     Constructors invoked by deserializers.
     Each constructor has a signature that matches the fields in at least one version (as sometimes
     there are different Kafka versions that are actually composed of precisely the same fields).
  #}
  {% for field in complex_type.fields %}
  const {{ field.field_declaration() }}_;{% endfor %}
  {% for constructor in complex_type.compute_constructors() %}
  // constructor used in versions: {{ constructor['versions'] }}
  {{ constructor['full_declaration'] }}{% endfor %}

  {# For every field that's used in version, just compute its size using an encoder. #}
  {% if complex_type.fields|length > 0 %}
  uint32_t computeSize(const EncodingContext& encoder) const {
    const int16_t api_version = encoder.apiVersion();
    uint32_t written{0};{% for field in complex_type.fields %}
    if (api_version >= {{ field.version_usage[0] }}
      && api_version < {{ field.version_usage[-1] + 1 }}) {
      written += encoder.computeSize({{ field.name }}_);
    }{% endfor %}
    return written;
  }
  {% else %}
  uint32_t computeSize(const EncodingContext&) const {
    return 0;
  }
  {% endif %}

  {# For every field that's used in version, just serialize it. #}
  {% if complex_type.fields|length > 0 %}
  uint32_t encode(Buffer::Instance& dst, EncodingContext& encoder) const {
    const int16_t api_version = encoder.apiVersion();
    uint32_t written{0};{% for field in complex_type.fields %}
    if (api_version >= {{ field.version_usage[0] }}
      && api_version < {{ field.version_usage[-1] + 1 }}) {
      written += encoder.encode({{ field.name }}_, dst);
    }{% endfor %}
    return written;
  }
  {% else %}
  uint32_t encode(Buffer::Instance&, EncodingContext&) const {
    return 0;
  }
  {% endif %}

  {% if complex_type.fields|length > 0 %}
  bool operator==(const {{ complex_type.name }}& rhs) const {
  {% else %}
  bool operator==(const {{ complex_type.name }}&) const {
  {% endif %}
    return true{% for field in complex_type.fields %}
    && {{ field.name }}_ == rhs.{{ field.name }}_{% endfor %};
  };

};

{#
  Each structure version has a deserializer that matches the structure's field list.
#}
{% for field_list in complex_type.compute_field_lists() %}
class {{ complex_type.name }}V{{ field_list.version }}Deserializer:
  public CompositeDeserializerWith{{ field_list.field_count() }}Delegates<
    {{ complex_type.name }}
    {% for field in field_list.used_fields() %},
      {{ field.deserializer_name_in_version(field_list.version) }}
    {% endfor %}>{};
{% endfor %}

